
export enum Inst {
    /** Add with carry: 加法 */
    ADC,
    /** AND memory with accumulator: AND 内存与累加器 */
    AND,
    /** Shift left one bit: 左移一位 */
    ASL,
    /** Branch on carry clear: 携带清除分支 */
    BCC,
    /** Branch on carry set: 携带设置分支 */
    BCS,
    /** Branch on zero: 零分支 */
    BEQ,
    BIT,
    /** Branch on negative result: 否定结果分支 */
    BMI,
    /** Branch on not zero: 非零分支 */
    BNE,
    /** Branch on positive result: 肯定结果分支 */
    BPL,
    BRK,
    /** Branch on overflow clear: 溢出清除分支 */
    BVC,
    /** Branch on overflow set: 溢出设置分支 */
    BVS,
    /** Clear carry flag: 清除携带标志 */
    CLC,
    /** Clear decimal flag: 清除十进制标志 */
    CLD,
    /** Clear interrupt flag: 清除中断标志 */
    CLI,
    /** Clear overflow flag: 清除溢出标志 */
    CLV,
    /** Compare memory and accumulator: 比较内存和累加器 */
    CMP,
    /** Compare memory and index X: 比较内存和索引 X */
    CPX,
    /** Compare memory and index Y: 比较内存和索引 Y */
    CPY,
    /** Decrement memory by one: 将内存减一 */
    DEC,
    /** Decrement index X by one: 将索引 X 减一 */
    DEX,
    /** Decrement index X by one: 将索引 Y 减一 */
    DEY,
    /** XOR Memory with accumulator, store in accumulator: 带累加器的 XOR 内存，存储在累加器中 */
    EOR,
    /** Increment memory by one: 内存增量1 */
    INC,
    /** Increment index X by one: 索引X增加1 */
    INX,
    /** Increment index Y by one: 索引Y增加1 */
    INY,
    /** Jump to new location: 跳转到新的位置 */
    JMP,
    /** Jump to new location, saving return address. Push return address on stack: 跳转到新的位置，保存返回地址。在堆栈上推送返回地址 */
    JSR,
    /** Load accumulator with memory: 内存载入累计器 */
    LDA,
    /** Load index X with memory: 用内存加载索引X */
    LDX,
    /** Load index Y with memory: 用内存加载索引Y */
    LDY,
    /** Shift right one bit: 右移一位 */
    LSR,
    /** No OPeration: 没有操作 */
    NOP,
    /** OR memory with accumulator, store in accumulator: 内存载入累计器 存入累进器 */
    ORA,
    /** Push accumulator on stack: 在堆栈上推累加器 */
    PHA,
    /** Push processor status on stack: 在堆栈上推送处理器状态 */
    PHP,
    /** Pull accumulator from stack: 从堆栈中拉出累加器 */
    PLA,
    /** Pull processor status from stack: 从堆栈中提取处理器状态 */
    PLP,
    /** Rotate one bit left: 向左旋转一位 */
    ROL,
    /** Rotate one bit right: 向右旋转一位 */
    ROR,
    /** Return from interrupt. Pull status and PC from stack: 从中断返回。从堆栈中拉出状态和PC */
    RTI,
    /** Return from subroutine. Pull PC from stack: 从子程序返回。从堆栈中取出PC */
    RTS,
    SBC,
    /** Set carry flag: 设置携带标记 */
    SEC,
    /** Set decimal mode: 设置十进制模式 */
    SED,
    /** Set interrupt disable status: 设置中断禁用状态 */
    SEI,
    /** Store accumulator in memory: 将累加器存储在内存中 */
    STA,
    /** Store index X in memory: 将索引 X 存储在内存中 */
    STX,
    /** Store index Y in memory: 将索引 Y 存储在内存中 */
    STY,
    /** Transfer accumulator to index X: 将累加器转移到索引 X */
    TAX,
    /** Transfer accumulator to index Y: 将累加器转移到索引 Y */
    TAY,
    /** Transfer stack pointer to index X: 将堆栈指针转移到索引 X */
    TSX,
    /** Transfer index X to accumulator: 将索引 X 转移到累加器 */
    TXA,
    /** Transfer index X to stack pointer: 将索引 X 传输到堆栈指针 */
    TXS,
    /** Transfer index Y to accumulator: 将索引 Y 传输到累加器 */
    TYA,
    INS_DUMMY, // dummy instruction used for 'halting' the processor some cycles
}

export enum Addr {
    ZP,
    REL,
    IMP,
    ABS,
    ACC,
    IMM,
    ZPX,
    ZPY,
    ABSX,
    ABSY,
    PREIDXIND,
    POSTIDXIND,
    INDABS,
}

// Generates and provides an array of details about instructions
export class OpData {
    private cycTable: number[];
    private addrDesc: string[];

    public opdata = new Uint32Array(256);

    constructor() {
        // Set all to invalid instruction (to detect crashes):
        for (let i = 0; i < 256; i++) {
            this.opdata[i] = 0xFF;
        }

        // Now fill in all valid opcodes:

        // ADC:
        this.setOp(Inst.ADC, 0x69, Addr.IMM, 2, 2);
        this.setOp(Inst.ADC, 0x65, Addr.ZP, 2, 3);
        this.setOp(Inst.ADC, 0x75, Addr.ZPX, 2, 4);
        this.setOp(Inst.ADC, 0x6D, Addr.ABS, 3, 4);
        this.setOp(Inst.ADC, 0x7D, Addr.ABSX, 3, 4);
        this.setOp(Inst.ADC, 0x79, Addr.ABSY, 3, 4);
        this.setOp(Inst.ADC, 0x61, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.ADC, 0x71, Addr.POSTIDXIND, 2, 5);

        // AND:
        this.setOp(Inst.AND, 0x29, Addr.IMM, 2, 2);
        this.setOp(Inst.AND, 0x25, Addr.ZP, 2, 3);
        this.setOp(Inst.AND, 0x35, Addr.ZPX, 2, 4);
        this.setOp(Inst.AND, 0x2D, Addr.ABS, 3, 4);
        this.setOp(Inst.AND, 0x3D, Addr.ABSX, 3, 4);
        this.setOp(Inst.AND, 0x39, Addr.ABSY, 3, 4);
        this.setOp(Inst.AND, 0x21, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.AND, 0x31, Addr.POSTIDXIND, 2, 5);

        // ASL:
        this.setOp(Inst.ASL, 0x0A, Addr.ACC, 1, 2);
        this.setOp(Inst.ASL, 0x06, Addr.ZP, 2, 5);
        this.setOp(Inst.ASL, 0x16, Addr.ZPX, 2, 6);
        this.setOp(Inst.ASL, 0x0E, Addr.ABS, 3, 6);
        this.setOp(Inst.ASL, 0x1E, Addr.ABSX, 3, 7);

        // BCC:
        this.setOp(Inst.BCC, 0x90, Addr.REL, 2, 2);

        // BCS:
        this.setOp(Inst.BCS, 0xB0, Addr.REL, 2, 2);

        // BEQ:
        this.setOp(Inst.BEQ, 0xF0, Addr.REL, 2, 2);

        // BIT:
        this.setOp(Inst.BIT, 0x24, Addr.ZP, 2, 3);
        this.setOp(Inst.BIT, 0x2C, Addr.ABS, 3, 4);

        // BMI:
        this.setOp(Inst.BMI, 0x30, Addr.REL, 2, 2);

        // BNE:
        this.setOp(Inst.BNE, 0xD0, Addr.REL, 2, 2);

        // BPL:
        this.setOp(Inst.BPL, 0x10, Addr.REL, 2, 2);

        // BRK:
        this.setOp(Inst.BRK, 0x00, Addr.IMP, 1, 7);

        // BVC:
        this.setOp(Inst.BVC, 0x50, Addr.REL, 2, 2);

        // BVS:
        this.setOp(Inst.BVS, 0x70, Addr.REL, 2, 2);

        // CLC:
        this.setOp(Inst.CLC, 0x18, Addr.IMP, 1, 2);

        // CLD:
        this.setOp(Inst.CLD, 0xD8, Addr.IMP, 1, 2);

        // CLI:
        this.setOp(Inst.CLI, 0x58, Addr.IMP, 1, 2);

        // CLV:
        this.setOp(Inst.CLV, 0xB8, Addr.IMP, 1, 2);

        // CMP:
        this.setOp(Inst.CMP, 0xC9, Addr.IMM, 2, 2);
        this.setOp(Inst.CMP, 0xC5, Addr.ZP, 2, 3);
        this.setOp(Inst.CMP, 0xD5, Addr.ZPX, 2, 4);
        this.setOp(Inst.CMP, 0xCD, Addr.ABS, 3, 4);
        this.setOp(Inst.CMP, 0xDD, Addr.ABSX, 3, 4);
        this.setOp(Inst.CMP, 0xD9, Addr.ABSY, 3, 4);
        this.setOp(Inst.CMP, 0xC1, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.CMP, 0xD1, Addr.POSTIDXIND, 2, 5);

        // CPX:
        this.setOp(Inst.CPX, 0xE0, Addr.IMM, 2, 2);
        this.setOp(Inst.CPX, 0xE4, Addr.ZP, 2, 3);
        this.setOp(Inst.CPX, 0xEC, Addr.ABS, 3, 4);

        // CPY:
        this.setOp(Inst.CPY, 0xC0, Addr.IMM, 2, 2);
        this.setOp(Inst.CPY, 0xC4, Addr.ZP, 2, 3);
        this.setOp(Inst.CPY, 0xCC, Addr.ABS, 3, 4);

        // DEC:
        this.setOp(Inst.DEC, 0xC6, Addr.ZP, 2, 5);
        this.setOp(Inst.DEC, 0xD6, Addr.ZPX, 2, 6);
        this.setOp(Inst.DEC, 0xCE, Addr.ABS, 3, 6);
        this.setOp(Inst.DEC, 0xDE, Addr.ABSX, 3, 7);

        // DEX:
        this.setOp(Inst.DEX, 0xCA, Addr.IMP, 1, 2);

        // DEY:
        this.setOp(Inst.DEY, 0x88, Addr.IMP, 1, 2);

        // EOR:
        this.setOp(Inst.EOR, 0x49, Addr.IMM, 2, 2);
        this.setOp(Inst.EOR, 0x45, Addr.ZP, 2, 3);
        this.setOp(Inst.EOR, 0x55, Addr.ZPX, 2, 4);
        this.setOp(Inst.EOR, 0x4D, Addr.ABS, 3, 4);
        this.setOp(Inst.EOR, 0x5D, Addr.ABSX, 3, 4);
        this.setOp(Inst.EOR, 0x59, Addr.ABSY, 3, 4);
        this.setOp(Inst.EOR, 0x41, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.EOR, 0x51, Addr.POSTIDXIND, 2, 5);

        // INC:
        this.setOp(Inst.INC, 0xE6, Addr.ZP, 2, 5);
        this.setOp(Inst.INC, 0xF6, Addr.ZPX, 2, 6);
        this.setOp(Inst.INC, 0xEE, Addr.ABS, 3, 6);
        this.setOp(Inst.INC, 0xFE, Addr.ABSX, 3, 7);

        // INX:
        this.setOp(Inst.INX, 0xE8, Addr.IMP, 1, 2);

        // INY:
        this.setOp(Inst.INY, 0xC8, Addr.IMP, 1, 2);

        // JMP:
        this.setOp(Inst.JMP, 0x4C, Addr.ABS, 3, 3);
        this.setOp(Inst.JMP, 0x6C, Addr.INDABS, 3, 5);

        // JSR:
        this.setOp(Inst.JSR, 0x20, Addr.ABS, 3, 6);

        // LDA:
        this.setOp(Inst.LDA, 0xA9, Addr.IMM, 2, 2);
        this.setOp(Inst.LDA, 0xA5, Addr.ZP, 2, 3);
        this.setOp(Inst.LDA, 0xB5, Addr.ZPX, 2, 4);
        this.setOp(Inst.LDA, 0xAD, Addr.ABS, 3, 4);
        this.setOp(Inst.LDA, 0xBD, Addr.ABSX, 3, 4);
        this.setOp(Inst.LDA, 0xB9, Addr.ABSY, 3, 4);
        this.setOp(Inst.LDA, 0xA1, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.LDA, 0xB1, Addr.POSTIDXIND, 2, 5);


        // LDX:
        this.setOp(Inst.LDX, 0xA2, Addr.IMM, 2, 2);
        this.setOp(Inst.LDX, 0xA6, Addr.ZP, 2, 3);
        this.setOp(Inst.LDX, 0xB6, Addr.ZPY, 2, 4);
        this.setOp(Inst.LDX, 0xAE, Addr.ABS, 3, 4);
        this.setOp(Inst.LDX, 0xBE, Addr.ABSY, 3, 4);

        // LDY:
        this.setOp(Inst.LDY, 0xA0, Addr.IMM, 2, 2);
        this.setOp(Inst.LDY, 0xA4, Addr.ZP, 2, 3);
        this.setOp(Inst.LDY, 0xB4, Addr.ZPX, 2, 4);
        this.setOp(Inst.LDY, 0xAC, Addr.ABS, 3, 4);
        this.setOp(Inst.LDY, 0xBC, Addr.ABSX, 3, 4);

        // LSR:
        this.setOp(Inst.LSR, 0x4A, Addr.ACC, 1, 2);
        this.setOp(Inst.LSR, 0x46, Addr.ZP, 2, 5);
        this.setOp(Inst.LSR, 0x56, Addr.ZPX, 2, 6);
        this.setOp(Inst.LSR, 0x4E, Addr.ABS, 3, 6);
        this.setOp(Inst.LSR, 0x5E, Addr.ABSX, 3, 7);

        // NOP:
        this.setOp(Inst.NOP, 0xEA, Addr.IMP, 1, 2);

        // ORA:
        this.setOp(Inst.ORA, 0x09, Addr.IMM, 2, 2);
        this.setOp(Inst.ORA, 0x05, Addr.ZP, 2, 3);
        this.setOp(Inst.ORA, 0x15, Addr.ZPX, 2, 4);
        this.setOp(Inst.ORA, 0x0D, Addr.ABS, 3, 4);
        this.setOp(Inst.ORA, 0x1D, Addr.ABSX, 3, 4);
        this.setOp(Inst.ORA, 0x19, Addr.ABSY, 3, 4);
        this.setOp(Inst.ORA, 0x01, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.ORA, 0x11, Addr.POSTIDXIND, 2, 5);

        // PHA:
        this.setOp(Inst.PHA, 0x48, Addr.IMP, 1, 3);

        // PHP:
        this.setOp(Inst.PHP, 0x08, Addr.IMP, 1, 3);

        // PLA:
        this.setOp(Inst.PLA, 0x68, Addr.IMP, 1, 4);

        // PLP:
        this.setOp(Inst.PLP, 0x28, Addr.IMP, 1, 4);

        // ROL:
        this.setOp(Inst.ROL, 0x2A, Addr.ACC, 1, 2);
        this.setOp(Inst.ROL, 0x26, Addr.ZP, 2, 5);
        this.setOp(Inst.ROL, 0x36, Addr.ZPX, 2, 6);
        this.setOp(Inst.ROL, 0x2E, Addr.ABS, 3, 6);
        this.setOp(Inst.ROL, 0x3E, Addr.ABSX, 3, 7);

        // ROR:
        this.setOp(Inst.ROR, 0x6A, Addr.ACC, 1, 2);
        this.setOp(Inst.ROR, 0x66, Addr.ZP, 2, 5);
        this.setOp(Inst.ROR, 0x76, Addr.ZPX, 2, 6);
        this.setOp(Inst.ROR, 0x6E, Addr.ABS, 3, 6);
        this.setOp(Inst.ROR, 0x7E, Addr.ABSX, 3, 7);

        // RTI:
        this.setOp(Inst.RTI, 0x40, Addr.IMP, 1, 6);

        // RTS:
        this.setOp(Inst.RTS, 0x60, Addr.IMP, 1, 6);

        // SBC:
        this.setOp(Inst.SBC, 0xE9, Addr.IMM, 2, 2);
        this.setOp(Inst.SBC, 0xE5, Addr.ZP, 2, 3);
        this.setOp(Inst.SBC, 0xF5, Addr.ZPX, 2, 4);
        this.setOp(Inst.SBC, 0xED, Addr.ABS, 3, 4);
        this.setOp(Inst.SBC, 0xFD, Addr.ABSX, 3, 4);
        this.setOp(Inst.SBC, 0xF9, Addr.ABSY, 3, 4);
        this.setOp(Inst.SBC, 0xE1, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.SBC, 0xF1, Addr.POSTIDXIND, 2, 5);

        // SEC:
        this.setOp(Inst.SEC, 0x38, Addr.IMP, 1, 2);

        // SED:
        this.setOp(Inst.SED, 0xF8, Addr.IMP, 1, 2);

        // SEI:
        this.setOp(Inst.SEI, 0x78, Addr.IMP, 1, 2);

        // STA:
        this.setOp(Inst.STA, 0x85, Addr.ZP, 2, 3);
        this.setOp(Inst.STA, 0x95, Addr.ZPX, 2, 4);
        this.setOp(Inst.STA, 0x8D, Addr.ABS, 3, 4);
        this.setOp(Inst.STA, 0x9D, Addr.ABSX, 3, 5);
        this.setOp(Inst.STA, 0x99, Addr.ABSY, 3, 5);
        this.setOp(Inst.STA, 0x81, Addr.PREIDXIND, 2, 6);
        this.setOp(Inst.STA, 0x91, Addr.POSTIDXIND, 2, 6);

        // STX:
        this.setOp(Inst.STX, 0x86, Addr.ZP, 2, 3);
        this.setOp(Inst.STX, 0x96, Addr.ZPY, 2, 4);
        this.setOp(Inst.STX, 0x8E, Addr.ABS, 3, 4);

        // STY:
        this.setOp(Inst.STY, 0x84, Addr.ZP, 2, 3);
        this.setOp(Inst.STY, 0x94, Addr.ZPX, 2, 4);
        this.setOp(Inst.STY, 0x8C, Addr.ABS, 3, 4);

        // TAX:
        this.setOp(Inst.TAX, 0xAA, Addr.IMP, 1, 2);

        // TAY:
        this.setOp(Inst.TAY, 0xA8, Addr.IMP, 1, 2);

        // TSX:
        this.setOp(Inst.TSX, 0xBA, Addr.IMP, 1, 2);

        // TXA:
        this.setOp(Inst.TXA, 0x8A, Addr.IMP, 1, 2);

        // TXS:
        this.setOp(Inst.TXS, 0x9A, Addr.IMP, 1, 2);

        // TYA:
        this.setOp(Inst.TYA, 0x98, Addr.IMP, 1, 2);

        this.cycTable = new Array(
            /*0x00*/ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
            /*0x10*/ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            /*0x20*/ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6,
            /*0x30*/ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            /*0x40*/ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6,
            /*0x50*/ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            /*0x60*/ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6,
            /*0x70*/ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            /*0x80*/ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
            /*0x90*/ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5,
            /*0xA0*/ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
            /*0xB0*/ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4,
            /*0xC0*/ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
            /*0xD0*/ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            /*0xE0*/ 2, 6, 3, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
            /*0xF0*/ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
        );

        this.addrDesc = new Array(
            "Zero Page",
            "Relative",
            "Implied",
            "Absolute",
            "Accumulator",
            "Immediate",
            "Zero Page,X",
            "Zero Page,Y",
            "Absolute,X",
            "Absolute,Y",
            "Preindexed Indirect",
            "Postindexed Indirect",
            "Indirect Absolute",
        );
    }

    setOp(inst: Inst, op: number, addr: Addr, size: number, cycles: number) {
        this.opdata[op] =
            ((inst & 0xFF)) |
            ((addr & 0xFF) << 8) |
            ((size & 0xFF) << 16) |
            ((cycles & 0xFF) << 24);
    }
}
